﻿using S7Cat.Enums;

namespace S7Cat.Protocol
{
    /// <summary>
    /// Provides a representation of the Transport Service Access Point, or TSAP in short. TSAP's are used
    /// to specify a client and server address. For most PLC types a default TSAP is available that allows
    /// connection from any IP and can be calculated using the rack and slot numbers.
    /// </summary>
    internal struct Tsap
    {
        /// <summary>
        /// First byte of the TSAP.
        /// </summary>
        public byte FirstByte { get; set; }

        /// <summary>
        /// Second byte of the TSAP.
        /// </summary>
        public byte SecondByte { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="Tsap" /> class using the specified values.
        /// </summary>
        /// <param name="firstByte">The first byte of the TSAP.</param>
        /// <param name="secondByte">The second byte of the TSAP.</param>
        public Tsap(byte firstByte, byte secondByte)
        {
            FirstByte = firstByte;
            SecondByte = secondByte;
        }
    }

    /// <summary>
    /// Implements a pair of TSAP addresses used to connect to a PLC.
    /// </summary>
    internal struct TsapPair
    {
        /// <summary>
        /// The local <see cref="Tsap" />.
        /// </summary>
        public Tsap Local { get; set; }

        /// <summary>
        /// The remote <see cref="Tsap" />
        /// </summary>
        public Tsap Remote { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="TsapPair" /> class using the specified local and
        /// remote TSAP.
        /// </summary>
        /// <param name="local">The local TSAP.</param>
        /// <param name="remote">The remote TSAP.</param>
        public TsapPair(Tsap local, Tsap remote)
        {
            Local = local;
            Remote = remote;
        }

        public static TsapPair GetDefaultTsapPair(CpuType cpuType, int rack, int slot)
        {
            return rack is < 0 or > 0x0f
                ? throw new ArgumentOutOfRangeException(
                    nameof(rack),
                    "Rack must be between 0 and 15."
                )
                : slot is < 0 or > 0x0f
                ? throw new ArgumentOutOfRangeException(
                    nameof(slot),
                    "Slot must be between 0 and 15."
                )
                : cpuType switch
                {
                    CpuType.S7200 => new TsapPair(new Tsap(0x10, 0x00), new Tsap(0x10, 0x01)),
                    CpuType.Logo0BA8 =>
                    // The actual values are probably on a per-project basis
                    new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x01, 0x02)),
                    CpuType.S7200Smart
                    or CpuType.S71200
                    or CpuType.S71500
                    or CpuType.S7300
                    or CpuType.S7400 =>
                    // Testing with S7 1500 shows only the remote TSAP needs to match. This might differ for other
                    // PLC types.
                    new TsapPair(new Tsap(0x01, 0x00), new Tsap(0x03, (byte)((rack << 5) | slot))),
                    _ => throw new ArgumentOutOfRangeException(nameof(cpuType), "Invalid CPU Type specified"),
                };
        }
    }
}
